home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
14439
/
14439.xpi
/
chrome
/
tabber.jar
/
content
/
multirow.js
< prev
next >
Wrap
Text File
|
2009-11-12
|
14KB
|
392 lines
/*
Multiple-row tab bar
Reference: Tab Kit by John Mellor
Latest revisions by Frank Yan - 2009.10.12
<license>
Tab Kit - http://jomel.me.uk/software/firefox/tabkit/
Copyright (c) 2007 John Mellor
This Firefox extension, Tab Kit, is free software; you can
redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your
option) any later version.
Tab Kit is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
</license>
*/
var multirow = new function _multirow() {
const mr = this;
var _tabContainer;
var _tabstrip;
var _tabInnerBox;
var _tabs;
var _prefObservers = {};
var initialized = false;
var appTabWidth = 30;
this.preInit = function preInit() {
mr.initialized = true;
mr.preInitUAStyleSheets();
mr.preInitFixDropIndex();
};
this.init = function init(event) {
_tabInnerBox.style.setProperty("opacity", "0", "important");
setTimeout(function() { multirow.redraw(); }, 0);
mr.initMultiRowTabs();
};
this.initPrefCheck = function initPrefCheck() {
_tabContainer = gBrowser.mTabContainer;
_tabstrip = _tabContainer.mTabstrip;
_tabInnerBox = document.getAnonymousElementByAttribute(_tabstrip._scrollbox, "class", "box-inherit scrollbox-innerbox");
_tabs = gBrowser.mTabs;
mr.initTabMinWidth();
mr.addPrefListener("tabberwocky.multirow", mr.lateMultiRow);
};
this.UAStyleSheets = [ "chrome://tabber/content/scrollbar.css" ];
this.preInitUAStyleSheets = function preInitUAStyleSheets() { // [Fx3only] it seems
var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
for each (var s in mr.UAStyleSheets) {
var uri = ios.newURI(s, null, null);
if (!sss.sheetRegistered(uri, sss.AGENT_SHEET))
sss.loadAndRegisterSheet(uri, sss.AGENT_SHEET);
}
};
this.lateMultiRow = function lateMultiRow() {
if (!gBrowser.mPrefs.getBoolPref("tabberwocky.multirow")) return;
if (mr.initialized) return;
mr.preInit();
mr.initMultiRowTabs();
};
this.initTabMinWidth = function initTabMinWidth() {
mr.addPrefListener("browser.tabs.tabMinWidth", mr.resetTabMinWidth);
};
this.resetTabMinWidth = function resetTabMinWidth(pref) {
mr.setTabMinWidth(gPrefService.getIntPref("browser.tabs.tabMinWidth"));
};
this.setTabMinWidth = function setTabMinWidth(minWidth) {
_tabContainer.mTabMinWidth = minWidth;
for (var i = 0; i < _tabs.length; i++)
if (_tabs[i].getAttribute("apptab") != "true")
_tabs[i].minWidth = minWidth;
else
_tabs[i].minWidth = _tabContainer.getAttribute("multirow") == "true" ? mr.appTabWidth :
(appTabs && appTabs.minWidth ? appTabs.minWidth : 0);
_tabContainer.adjustTabstrip();
};
this.addPrefListener = function addPrefListener(prefString, prefListener) {
if (!_prefObservers[prefString]) {
_prefObservers[prefString] = {
listeners: [],
register: function() {
gPrefService.addObserver(prefString, this, false);
},
unregister: function() {
gPrefService.removeObserver(prefString, this);
},
observe: function(aSubject, aTopic, aData) {
if (aTopic != "nsPref:changed") return;
for each (var listener in this.listeners) {
listener(aData);
}
}
};
window.addEventListener("unload", function() { _prefObservers[prefString].unregister(); }, false);
_prefObservers[prefString].register();
}
_prefObservers[prefString].listeners.push(prefListener);
};
this.scrollToElement = function scrollToElement(overflowPane, element) {
if (_tabContainer.getAttribute("multirowscroll") != "true") return;
var scrollbar = overflowPane.mVerticalScrollbar;
if (!scrollbar) return;
var container = element.parentNode;
var firstChild = container.firstChild;
while (firstChild.hidden ||
document.defaultView.getComputedStyle(firstChild, null).getPropertyValue("visibility") != "visible")
firstChild = firstChild.nextSibling;
var lastChild = container.lastChild;
while (lastChild.hidden ||
document.defaultView.getComputedStyle(lastChild, null).getPropertyValue("visibility") != "visible")
lastChild = lastChild.previousSibling;
var curpos = parseInt(scrollbar.getAttribute("curpos"));
if (isNaN(curpos)) curpos = 0;
var firstY = firstChild.boxObject.y;
var elemY = element.boxObject.y;
var lastY = lastChild.boxObject.y;
var height = element.boxObject.height;
var relY = elemY - firstY;
var paneHeight = overflowPane.boxObject.height;
// Make sure overflowPane is never scrolled halfway across elements at both the top and bottom
if ((lastY - firstY) % height == 0 && curpos % height != 0 && (curpos + paneHeight + firstY - lastY) % height != 0)
curpos = height * Math.round(curpos / height);
var minpos = relY;
if (minpos < curpos) curpos = minpos;
else {
var maxpos = relY + height - paneHeight;
if (maxpos > curpos) curpos = maxpos;
}
scrollbar.setAttribute("curpos", curpos);
};
/// Initialisation:
this.initMultiRowTabs = function initMultiRowTabs() {
mr.addPrefListener("tabberwocky.multirow", mr.updateMultiRowTabs);
mr.addPrefListener("tabberwocky.maxrows", mr.updateMultiRowTabs);
mr.addPrefListener("tabberwocky.newtabbutton", mr.updateMultiRowTabs);
mr.addPrefListener("browser.tabs.tabMinWidth", mr.updateMultiRowTabs);
mr.addPrefListener("browser.tabs.closeButtons", mr.delayedUpdate);
_tabstrip.addEventListener("overflow", mr._preventMultiRowFlowEvent, true);
_tabstrip.addEventListener("underflow", mr._preventMultiRowFlowEvent, true);
_tabContainer.addEventListener("TabOpen", mr.updateMultiRowTabs, false);
_tabContainer.addEventListener("TabClose", mr.delayedUpdate, false);
document.addEventListener("SSTabRestoring", mr.updateMultiRowTabs, false); // "hidden" attributes might be restored!
window.addEventListener("resize", mr.updateMultiRowTabs, false);
_tabContainer.addEventListener("TabSelect", mr.multiRow_onTabSelect, false);
_tabContainer.addEventListener("TabMove", mr.multiRow_onTabSelect, false); // In case a tab is moved out of sight
mr.updateMultiRowTabs();
};
this.delayedUpdate = function delayedUpdate() {
setTimeout(function() { multirow.updateMultiRowTabs(); }, 0);
};
this.redraw = function redraw() {
_tabInnerBox.style.setProperty("opacity", "1", "important");
_tabInnerBox.removeAttribute("style");
if (_tabContainer.getAttribute("multirow") == "true")
setTimeout(function() { multirow.multiRow_onTabSelect(); }, 0);
};
/// Event Listeners:
this.updateMultiRowTabs = function updateMultiRowTabs() {
var needsDisabling = false, needsScrolling = false;
if (gBrowser.mPrefs.getBoolPref("tabberwocky.multirow")) {
_tabContainer.removeAttribute("overflow");
_tabContainer.setAttribute("newatend", "true");
if (!gBrowser.getStripVisibility()) {
var rows = 0;
}
else {
var visibleTabs = _tabs.length, apps = 0, firstApp = -1;
for (var i = 0; i < _tabs.length; i++) {
if (_tabs[i].hidden)
visibleTabs--;
if (_tabs[i].getAttribute("apptab") == "true") {
if (firstApp == -1) {
firstApp = i;
if (!mr.appTabWidth) {
var f = document.getAnonymousElementByAttribute(_tabs[i], "class", "tab-icon-image");
mr.appTabWidth = 16 +
mr.getIntStyle(f, "padding-left") + mr.getIntStyle(f, "padding-right") +
mr.getIntStyle(f, "margin-left") + mr.getIntStyle(f, "margin-right") +
mr.getIntStyle(_tabs[i], "padding-left") + mr.getIntStyle(_tabs[i], "padding-right") +
mr.getIntStyle(_tabs[i], "border-left-width") + mr.getIntStyle(_tabs[i], "border-right-width");
if (mr.appTabWidth < 30) mr.appTabWidth = 30;
}
}
apps++;
}
}
var availWidth = _tabstrip._scrollbox.boxObject.width;
availWidth -= mr.getIntStyle(_tabstrip._scrollbox, "padding-left");
availWidth -= mr.getIntStyle(_tabstrip._scrollbox, "padding-right");
var minWidth = gPrefService.getIntPref("browser.tabs.tabMinWidth");
var tabsPerRow = Math.floor(availWidth / minWidth);
var tab = (_tabs.length > 1) ? 1 : 0;
var margin = mr.getIntStyle(_tabs[tab], "margin-left") + mr.getIntStyle(_tabs[tab], "margin-right");
while (tabsPerRow * (minWidth + margin) > availWidth)
tabsPerRow--;
if (margin < 0) margin /= 2;
if (apps) {
var w = (availWidth - (margin * tabsPerRow)) / tabsPerRow;
if (minWidth > w) w = minWidth;
var t = Math.floor((w - mr.appTabWidth) * apps / w);
if (t > 0)
visibleTabs -= t;
}
if (margin < 0) visibleTabs -= margin / minWidth;
var rows = Math.ceil(visibleTabs / tabsPerRow);
}
if (rows > 1) {
if (_tabContainer.getAttribute("multirow") != "true") {
_tabContainer.setAttribute("multirow", "true");
gBrowser.mTabBox.setAttribute("dropindicator-hidden", "true");
try {
_tabstrip._scrollBoxObject.scrollTo(0, 0);
}
catch (ex) {}
}
var maxRows = gBrowser.mPrefs.getIntPref("tabberwocky.maxrows");
if (rows > maxRows) {
_tabContainer.setAttribute("multirowscroll", "true");
// make sure tab borders and padding are properly taken into account...
_tabstrip.style.setProperty("min-height", 24 * maxRows + "px", "important");
_tabstrip.style.setProperty("max-height", 24 * maxRows + "px", "important");
var scrollbar = _tabInnerBox.mVerticalScrollbar;
try {
scrollbar.removeEventListener("DOMAttrModified", mr.preventChangeOfAttributes, true);
}
catch (ex) {}
try {
scrollbar.setAttribute("increment", 24);
scrollbar.setAttribute("pageincrement", 48);
scrollbar.addEventListener("DOMAttrModified", mr.preventChangeOfAttributes, true);
availWidth -= Math.max(scrollbar.boxObject.width, 22);
}
catch (ex) { // scrollbar not yet created
availWidth -= 22;
}
}
else {
_tabContainer.removeAttribute("multirowscroll");
_tabstrip.style.setProperty("min-height", 24 * rows + "px", "important");
_tabstrip.style.setProperty("max-height", 24 * rows + "px", "important");
}
mr.setTabMinWidth((availWidth - (margin * tabsPerRow)) / tabsPerRow);
if (rows > maxRows)
mr.multiRow_onTabSelect(); // check if we need to scroll
// check if we need to redraw
if (_tabInnerBox.boxObject.screenY != _tabInnerBox.parentNode.boxObject.screenY) {
_tabInnerBox.setAttribute("style", "display:inline !important");
setTimeout(function() { multirow.redraw(); }, 0);
}
}
else {
if (_tabContainer.getAttribute("multirow") == "true") {
needsDisabling = true;
needsScrolling = true;
}
_tabContainer.setAttribute("multirow", "false");
}
}
else if (_tabContainer.hasAttribute("multirow")) {
_tabContainer.removeAttribute("multirowscroll");
_tabContainer.removeAttribute("newatend");
needsDisabling = true;
needsScrolling = (_tabContainer.getAttribute("multirow") == "true");
_tabContainer.removeAttribute("multirow");
}
if (needsDisabling) {
gBrowser.mTabBox.removeAttribute("dropindicator-hidden");
mr.resetTabMinWidth();
if (needsScrolling) {
try {
_tabstrip._scrollBoxObject.ensureElementIsVisible(gBrowser.selectedTab);
}
catch (ex) {}
}
_tabstrip.style.removeProperty("min-height");
_tabstrip.style.removeProperty("max-height");
_tabContainer.adjustTabstrip();
}
};
this.getIntStyle = function getIntStyle(node, property) {
return parseInt(document.defaultView.getComputedStyle(node, null).getPropertyValue(property));
};
this.preventChangeOfAttributes = function preventChangeOfAttributes(event) {
var scrollbar = _tabInnerBox.mVerticalScrollbar;
if (event.attrName == "increment") {
scrollbar.setAttribute("increment", 24);
event.stopPropagation();
}
else if (event.attrName == "pageincrement") {
scrollbar.setAttribute("pageincrement", 48);
event.stopPropagation();
}
};
this.multiRow_onTabSelect = function multiRow_onTabSelect() {
mr.scrollToElement(_tabInnerBox, gBrowser.selectedTab);
};
/// Method hooks:
this.preInitFixDropIndex = function preInitFixDropIndex(event) {
tkLib.addMethodHook([
"gBrowser.getNewIndex",
null,
'{',
'{ \
var multiRow = this.mTabContainer.getAttribute("multirow") == "true";',
'aEvent.screenX < this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2', // ltr
'(multiRow && aEvent.screenY < this.mTabs[i].boxObject.screenY) \
|| (aEvent.screenX < this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2 \
&& (aEvent.screenY < this.mTabs[i].boxObject.screenY + this.mTabs[i].boxObject.height \
|| !multiRow))',
'aEvent.screenX > this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2', // rtl
'(multiRow && aEvent.screenY < this.mTabs[i].boxObject.screenY) \
|| (aEvent.screenX > this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2 \
&& (aEvent.screenY < this.mTabs[i].boxObject.screenY + this.mTabs[i].boxObject.height \
|| !multiRow))'
]);
};
/// Private Methods
this._preventMultiRowFlowEvent = function _preventMultiRowFlowEvent(event) {
if (_tabContainer.hasAttribute("multirow")) {
event.preventDefault();
event.stopPropagation();
}
};
};
window.addEventListener("load", multirow.initPrefCheck, false);
if (gPrefService.getBoolPref('tabberwocky.multirow')) {
window.addEventListener("DOMContentLoaded", multirow.preInit, false);
window.addEventListener("load", multirow.init, false);
}